﻿#include "WatchDog.h"
#include "qdebug.h"
#include "qdatetime.h"
#include "qthread.h"
#include "QMetaEnum"
#pragma execution_character_set("utf-8")

WatchDog::WatchDog(QObject* parent) : QObject(parent)
{
    p.setProcessChannelMode(QProcess::ForwardedChannels);   //从主程序输出
    p.setReadChannel(QProcess::StandardOutput);
    QObject::connect(&p, &QProcess::readyReadStandardOutput, this, [=]() {
        qDebug() << p.readAllStandardOutput();
        });
    QObject::connect(&p, &QProcess::readyReadStandardError, this, [=]() {
        qDebug() << p.readAllStandardError();
        });
    QObject::connect(&p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) {

        QMetaEnum metaEnum = QMetaEnum::fromType<QProcess::ExitStatus>();
        qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss : 退出 exitCode:") << QString::number(exitCode) << "  exitStatus:" << QString(metaEnum.valueToKey(exitStatus));
        // 等待重新启动子进程
        if (waitTime > 0)
        {
            if (info)
            {
                qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss : 等待").toUtf8().data() << QString::number(waitTime).toUtf8().data() << "ms";
            }
            QThread::msleep(waitTime);
            if (info)
            {
                qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss : 等待完成").toUtf8().data();
            }
        }

        if (l.count() > 0)
        {
            if (info)
            {
                qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss :").toUtf8().data() << "Restart->" + w + "; l = " << l << ";";
            }
            p.start(w, l);
        }
        else
        {
            if (info)
            {
                qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss :").toUtf8().data() << "Restart->" + w + "; f = " << f << ";";
            }
            p.start(w, f);
        }

        });
    
}

WatchDog::~WatchDog()
{
    if (shm.isAttached())
    {
        shm.detach();
    }
}
bool WatchDog::setArgv(int& argc, char** argv)
{
    CMDIndex cmdIndex = argv_switch;
    for (int i = 1; i < argc; i++)
    {
        QString cmd = argv[i];

        switch (cmdIndex)
        {
        case argv_w:
        {
            w = argv[i];
            cmdIndex = argv_switch;
        }
        break;
        case argv_f:
        {
            if (cmd.startsWith("-"))
                goto cmd_switch;

            f << argv[i];
            //cmdIndex = argv_switch;
        }
        break;
        case argv_l:
        {
            if (cmd.startsWith("-"))
                goto cmd_switch;

            l << argv[i];
            //cmdIndex = argv_switch;
        }
        break;
        case argv_waitTime:
        {
            waitTime = QString(argv[i]).toUInt();
            cmdIndex = argv_switch;
        }
        break;
        case argv_shmName:
        {
            shmName = argv[i];
            cmdIndex = argv_switch;
        }
        break;
        case argv_shmCheckInterval:
        {
            shmCheckInterval = QString(argv[i]).toUInt();
            if (shmCheckInterval < 100)
            {
                shmCheckInterval = 100;
                qDebug() << "共享内存检查间隔最小为100ms";
            }
            cmdIndex = argv_switch;
        }
        break;
        case argv_shmCheckErrorValue:
        {
            shmCheckErrorValue = QString(argv[i]).toUInt();
            cmdIndex = argv_switch;
        }
        break;
        case argv_switch:
        {
        cmd_switch:

            if (cmd == "-w")    //需要守护的线程
            {
                cmdIndex = argv_w;
            }
            else if (cmd == "-f")   //第一次启动时需要传递的参数
            {
                cmdIndex = argv_f;
            }
            else if (cmd == "-l")   //出错后重启时需要传递的参数
            {
                cmdIndex = argv_l;
            }
            else if (cmd == "-t")   //重启等待时间(ms)
            {
                cmdIndex = argv_waitTime;
            }
            else if (cmd == "-i")   //是否输出信息
            {
                info = true;
                cmdIndex = argv_switch;
            }
            else if (cmd == "-s")   //共享内存名称
            {
                cmdIndex = argv_shmName;
            }
            else if (cmd == "-sci")   //设置共享内存检查间隔
            {
                cmdIndex = argv_shmCheckInterval;
            }
            else if (cmd == "-sce")   //设置共享内存检查错误阈值
            {
                cmdIndex = argv_shmCheckErrorValue;
            }
            else if (cmd == "-h")   //help信息
            {
                qDebug() << "-w 指定目标程序 绝对路径,";
                qDebug() << "-f 第一次启动时需要传递给目标程序的参数,可设置多个,用空格分隔";
                qDebug() << "-l 出错后重启时需要传递给目标程序的参数,可设置多个,用空格分隔";
                qDebug() << "-t 出错后重启时需要等待的时间(ms),默认0ms";
                qDebug() << "-i 是否输出信息";
                qDebug() << "-s 设置共享内存Key";
                qDebug() << "-sci 设置共享内存检查间隔(ms),默认1000ms";
                qDebug() << "-sce 设置共享内存检查错误阈值,默认3";
                qDebug() << "启用共享内存后，必须定时更新共享内存前四个字节的内容,否则超时后会重启目标程序";
                return false;
            }
            else
            {
                qDebug() << "参数错误:" + cmd;
                return false;
            }
        }
        break;
        default:
            qDebug() << "参数错误!";
            return false;
            break;
        }

    }
    if (w == "")
    {
        qDebug() << "请使用-w指定目标程序";
        return false;
    }

    // 运行子进程代码
    if (info)
    {
        qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss :").toUtf8().data() << "Start->" + w + "; f = " << f << "; ";
    }

    if (shmName != "")
    {
        shm.setKey(shmName);
        if (!shm.create(4, QSharedMemory::ReadOnly))
        {
            if (shm.error() == QSharedMemory::AlreadyExists)
            {
                if (!shm.attach(QSharedMemory::ReadOnly))
                {
                    qDebug() << "无法关联共享内存！";
                    return false;
                }
            }
            else
            {
                qDebug() << "无法创建共享内存！";
                return false;
            }
            
            
        }

        startTimer(shmCheckInterval);
    }
    p.start(w, f);
    return true;
}

void WatchDog::timerEvent(QTimerEvent* event)
{

    if (shm.isAttached()) 
    {
        //shm.lock();
        uint32_t* data = (uint32_t*)shm.constData();
        if (shmDataImage != *data)
        {
            shmDataImage = *data;
            shmDataCounter = 0;
        }
        else
        {
            shmDataCounter++;
            if (shmDataCounter > shmCheckErrorValue)
            {
                shmDataCounter = 0;
                qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss :").toUtf8().data() << "shmCheckError->" " kill ";
                p.kill();
            }
        }
        //shm.unlock();
    }
    
}
